一、概述
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高 效率而生。
官网:https://mybatis.plus/ 或 https://mp.baomidou.com/
文档地址:https://mybatis.plus/guide/
源码地址:https://github.com/baomidou/mybatis-plus
二、使用示例
2.1 与springboot整合
依赖
runtimeOnly 'mysql:mysql-connector-java:8.0.15'
compile 'com.baomidou:mybatis-plus-boot-starter:3.4.3'
compile 'com.alibaba:druid-spring-boot-starter:1.2.6'配置
# 数据源配置
<NolebasePageProperties />
spring:
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/ruoyi_vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
username: root
password: 1234
druid:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 注意这个值和druid原生不一致,默认启动了stat
filters: stat
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: ruoyi
login-password: 123456
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# MyBatisPlus配置
# https://baomidou.com/config/
mybatis-plus:
# 不支持多包, 如有需要可在注解配置 或 提升扫包等级
# 例如 com.**.**.mapper
# mapperPackage: com.ruoyi.**.mapper
# 对应的 XML 文件位置
mapperLocations: classpath*:**/*Mapper.xml
# 实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: com.lly.**.model
# 针对 typeAliasesPackage,如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象
#typeAliasesSuperType: Class<?>
# 如果配置了该属性,SqlSessionFactoryBean 会把该包下面的类注册为对应的 TypeHandler
#typeHandlersPackage: null
# 如果配置了该属性,会将路径下的枚举类进行注入,让实体类字段能够简单快捷的使用枚举属性
#typeEnumsPackage: null
# 启动时是否检查 MyBatis XML 文件的存在,默认不检查
checkConfigLocation: false
# 通过该属性可指定 MyBatis 的执行器,MyBatis 的执行器总共有三种:
# SIMPLE:该执行器类型不做特殊的事情,为每个语句的执行创建一个新的预处理语句(PreparedStatement)
# REUSE:该执行器类型会复用预处理语句(PreparedStatement)
# BATCH:该执行器类型会批量执行所有的更新语句
executorType: SIMPLE
# 指定外部化 MyBatis Properties 配置,通过该配置可以抽离配置,实现不同环境的配置部署
configurationProperties: null
configuration:
# 自动驼峰命名规则(camel case)映射
# 如果您的数据库命名符合规则无需使用 @TableField 注解指定数据库字段名
mapUnderscoreToCamelCase: true
# 默认枚举处理类,如果配置了该属性,枚举将统一使用指定处理器进行处理
# org.apache.ibatis.type.EnumTypeHandler : 存储枚举的名称
# org.apache.ibatis.type.EnumOrdinalTypeHandler : 存储枚举的索引
# com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler : 枚举类需要实现IEnum接口或字段标记@EnumValue注解.
defaultEnumTypeHandler: org.apache.ibatis.type.EnumTypeHandler
# 当设置为 true 的时候,懒加载的对象可能被任何懒属性全部加载,否则,每个属性都按需加载。需要和 lazyLoadingEnabled 一起使用。
aggressiveLazyLoading: true
# MyBatis 自动映射策略
# NONE:不启用自动映射
# PARTIAL:只对非嵌套的 resultMap 进行自动映射
# FULL:对所有的 resultMap 都进行自动映射
autoMappingBehavior: PARTIAL
# MyBatis 自动映射时未知列或未知属性处理策
# NONE:不做任何处理 (默认值)
# WARNING:以日志的形式打印相关警告信息
# FAILING:当作映射失败处理,并抛出异常和详细信息
autoMappingUnknownColumnBehavior: NONE
# Mybatis一级缓存,默认为 SESSION
# SESSION session级别缓存,同一个session相同查询语句不会再次查询数据库
# STATEMENT 关闭一级缓存
localCacheScope: SESSION
# 开启Mybatis二级缓存,默认为 true
cacheEnabled: false
# 更详细的日志输出 会有性能损耗
# logImpl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
# 是否打印 Logo banner
banner: true
# 是否初始化 SqlRunner
enableSqlRunner: false
dbConfig:
# 主键类型
# AUTO 数据库ID自增
# NONE 空
# INPUT 用户输入ID
# ASSIGN_ID 全局唯一ID
# ASSIGN_UUID 全局唯一ID UUID
idType: AUTO
# 表名前缀
tablePrefix: null
# 字段 format,例: %s,(对主键无效)
columnFormat: null
# 表名是否使用驼峰转下划线命名,只对表名生效
tableUnderline: true
# 大写命名,对表名和字段名均生效
capitalMode: false
# 全局的entity的逻辑删除字段属性名
logicDeleteField: null
# 逻辑已删除值
logicDeleteValue: 2
# 逻辑未删除值
logicNotDeleteValue: 0
# 字段验证策略之 insert,在 insert 的时候的字段验证策略
# IGNORED 忽略判断
# NOT_NULL 非NULL判断
# NOT_EMPTY 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)
# DEFAULT 默认的,一般只用于注解里
# NEVER 不加入 SQL
insertStrategy: NOT_EMPTY
# 字段验证策略之 update,在 update 的时候的字段验证策略
updateStrategy: NOT_EMPTY
# 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
selectStrategy: NOT_EMPTY配置类
package com.lly.db.mybatisplus.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.IllegalSQLInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.lly.db.mybatisplus.config.handler.CreateAndUpdateMetaObjectHandler;
import com.lly.db.mybatisplus.core.methods.InsertAll;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.util.List;
/**
* @Classname MybatisPlusConfig
* @Description mybatisPlus配置类
* @Date 2021/07/18 13:18
* @Created by wxp
*/
@Component
@EnableTransactionManagement(proxyTargetClass = true)
public class MybatisPlusConfig {
/**
* 一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//向Mybatis过滤器链中添加分页拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 乐观锁插件
interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
//还可以添加i他的拦截器
return interceptor;
}
/**
* 乐观锁插件
* https://baomidou.com/guide/interceptor-optimistic-locker.html
*/
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
return new OptimisticLockerInnerInterceptor();
}
/**
* 如果是对全表的删除或更新操作,就会终止该操作
* https://baomidou.com/guide/interceptor-block-attack.html
*/
// public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {
// return new BlockAttackInnerInterceptor();
// }
/**
* sql性能规范插件(垃圾SQL拦截)
* 如有需要可以启用
*/
// public IllegalSQLInnerInterceptor illegalSQLInnerInterceptor() {
// return new IllegalSQLInnerInterceptor();
// }
/**
* 自定义主键策略
* https://baomidou.com/guide/id-generator.html
*/
// @Bean
// public IdentifierGenerator idGenerator() {
// return new CustomIdGenerator();
// }
/**
* 元对象字段填充控制器
* 可对执行的sql进行扩展,如:添加更新时间等操作
* https://baomidou.com/guide/auto-fill-metainfo.html
*/
@Bean
public MetaObjectHandler metaObjectHandler() {
return new CreateAndUpdateMetaObjectHandler();
}
/**
* sql注入器配置
* 可对BaseMapper中的方法进行拓展,如批量插入
* https://baomidou.com/guide/sql-injector.html
*/
@Bean
public ISqlInjector sqlInjector() {
return new DefaultSqlInjector() {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
methodList.add(new InsertAll());
return methodList;
}
};
}
/**
* TenantLineInnerInterceptor 多租户插件
* https://baomidou.com/guide/interceptor-tenant-line.html
* DynamicTableNameInnerInterceptor 动态表名插件
* https://baomidou.com/guide/interceptor-dynamic-table-name.html
*/
}自动填充更新时间
package com.lly.db.mybatisplus.config.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.lly.datacover.exception.MicroRuntimeException;
import com.lly.db.mybatisplus.DBError;
import org.apache.http.HttpStatus;
import org.apache.ibatis.reflection.MetaObject;
import java.util.Date;
/**
* MP注入处理器
*
* @author Lion Li
* @date 2021/4/25
*/
public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
try {
//根据属性名字设置要填充的值
if (metaObject.hasGetter("createTime")) {
if (metaObject.getValue("createTime") == null) {
this.setFieldValByName("createTime", new Date(), metaObject);
}
}
// if (metaObject.hasGetter("createBy")) {
// if (metaObject.getValue("createBy") == null) {
// this.setFieldValByName("createBy", SecurityUtils.getUsername(), metaObject);
// }
// }
} catch (Exception e) {
// throw new MicroRuntimeException("自动注入异常 => " + e.getMessage(), HttpStatus.SC_UNAUTHORIZED);
throw new MicroRuntimeException(DBError.auto_fail,"自动注入异常 => " + e.getMessage());
}
}
@Override
public void updateFill(MetaObject metaObject) {
try {
// if (metaObject.hasGetter("updateBy")) {
// if (metaObject.getValue("updateBy") == null) {
// this.setFieldValByName("updateBy", SecurityUtils.getUsername(), metaObject);
// }
// }
if (metaObject.hasGetter("updateTime")) {
if (metaObject.getValue("updateTime") == null) {
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
} catch (Exception e) {
throw new MicroRuntimeException(DBError.auto_fail,"自动注入异常 => " + e.getMessage());
}
}
}批量插入
package com.lly.db.mybatisplus.core.methods;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.lly.common.utils.StringUtils;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
/**
* @Classname InsertAll
* @Description TODO
* @Date 2021/07/18 14:42
* @Created by wxp
*/
public class InsertAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
final String sql = "<script>insert into %s %s values %s</script>";
final String fieldSql = prepareFieldSql(tableInfo);
final String valueSql = prepareValuesSqlForMysqlBatch(tableInfo);
KeyGenerator keyGenerator = new NoKeyGenerator();
SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
String keyProperty = null;
String keyColumn = null;
// 表包含主键处理逻辑,如果不包含主键当普通字段处理
if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) {
if (tableInfo.getIdType() == IdType.AUTO) {
/** 自增主键 */
keyGenerator = new Jdbc3KeyGenerator();
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
} else {
if (null != tableInfo.getKeySequence()) {
keyGenerator = TableInfoHelper.genKeyGenerator(getMethod(sqlMethod), tableInfo, builderAssistant);
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
}
}
}
final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, "insertAll", sqlSource, keyGenerator, keyProperty, keyColumn);
}
private String prepareFieldSql(TableInfo tableInfo) {
StringBuilder fieldSql = new StringBuilder();
if (StringUtils.isNotBlank(tableInfo.getKeyColumn())) {
fieldSql.append(tableInfo.getKeyColumn()).append(",");
}
tableInfo.getFieldList().forEach(x -> fieldSql.append(x.getColumn()).append(","));
fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
fieldSql.insert(0, "(");
fieldSql.append(")");
return fieldSql.toString();
}
private String prepareValuesSqlForMysqlBatch(TableInfo tableInfo) {
final StringBuilder valueSql = new StringBuilder();
valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
if (StringUtils.isNotBlank(tableInfo.getKeyColumn())) {
valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
}
tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
valueSql.delete(valueSql.length() - 1, valueSql.length());
valueSql.append("</foreach>");
return valueSql.toString();
}
}使用示例
package com.note.technology.mybatisplus.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.note.technology.mybatisplus.model.DemoModel;
/**
* @Classname TestMapper
* @Description TODO
* 需使用@MapperScan("com.note.technology.mybatisplus.mapper") 配置扫描的mapper路径
*/
public interface TestMapper extends BaseMapper<DemoModel> {
}package com.note.technology.mybatisplus.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* @Classname DemoModel
* @Description TODO
*/
@TableName("demo")
@Data
public class DemoModel {
// 自增长id
@TableId(value = "ID", type = IdType.AUTO)
private Integer id;
private String name;
private String desc;
}2.2 抽取Base接口和服务类
BaseDao
package com.lly.db.mybatisplus.core.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
/**
* @Classname IMapper
* @Description 继承baseMapper 并添加 批量插入的方法(需注入批量添加的sql注入器)
* @Date 2021/07/18 14:43
* @Created by wxp
*/
public interface IBaseDao<T> extends BaseMapper<T> {
int insertAll(@Param("list") Collection<T> batchList);
}Iservice接口
添加转换为vo的方法及分页对象转换的方法
package com.lly.db.mybatisplus.core.service;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lly.common.utils.BeanUtils;
import com.lly.datacover.page.Order;
import com.lly.datacover.page.PageQuery;
import com.lly.datacover.page.Pager;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @Classname IService
* @Description TODO
* @Date 2021/07/18 14:46
* @Created by wxp
*/
public interface IService<T> extends com.baomidou.mybatisplus.extension.service.IService<T> {
boolean saveAll(Collection<T> entityList);
/**
* 根据 ID 查询
*
* @param kClass vo类型
* @param id 主键ID
*/
default <K> K getVoById(Serializable id, Class<K> kClass) {
T t = getBaseMapper().selectById(id);
return BeanUtils.toBean(t, kClass);
}
/**
* 根据 ID 查询
*
* @param id 主键ID
* @param convertor 转换函数
* @param <K> vo类型
*/
default <K> K getVoById(Serializable id, Function<T, K> convertor) {
T t = getBaseMapper().selectById(id);
return convertor.apply(t);
}
/**
* 查询(根据ID 批量查询)
*
* @param kClass vo类型
* @param idList 主键ID列表
*/
default <K> List<K> listVoByIds(Collection<? extends Serializable> idList, Class<K> kClass) {
List<T> list = getBaseMapper().selectBatchIds(idList);
if (list == null) {
return null;
}
return list.stream()
.map(any -> BeanUtils.toBean(any, kClass))
.collect(Collectors.toList());
}
/**
* 查询(根据ID 批量查询)
*
* @param convertor 转换函数
* @param idList 主键ID列表
*/
default <K> List<K> listVoByIds(Collection<? extends Serializable> idList,
Function<Collection<T>, List<K>> convertor) {
List<T> list = getBaseMapper().selectBatchIds(idList);
if (list == null) {
return null;
}
return convertor.apply(list);
}
/**
* 查询(根据 columnMap 条件)
*
* @param kClass vo类型
* @param columnMap 表字段 map 对象
*/
default <K> List<K> listVoByMap(Map<String, Object> columnMap, Class<K> kClass) {
List<T> list = getBaseMapper().selectByMap(columnMap);
if (list == null) {
return null;
}
return list.stream()
.map(any -> BeanUtils.toBean(any, kClass))
.collect(Collectors.toList());
}
/**
* 查询(根据 columnMap 条件)
*
* @param convertor 转换函数
* @param columnMap 表字段 map 对象
*/
default <K> List<K> listVoByMap(Map<String, Object> columnMap,
Function<Collection<T>, List<K>> convertor) {
List<T> list = getBaseMapper().selectByMap(columnMap);
if (list == null) {
return null;
}
return convertor.apply(list);
}
/**
* 根据 Wrapper,查询一条记录 <br/>
* <p>结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")</p>
*
* @param kClass vo类型
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
default <K> K getVoOne(Wrapper<T> queryWrapper, Class<K> kClass) {
return BeanUtils.toBean(getOne(queryWrapper, true), kClass);
}
/**
* 根据 Wrapper,查询一条记录 <br/>
* <p>结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")</p>
*
* @param convertor 转换函数
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
default <K> K getVoOne(Wrapper<T> queryWrapper, Function<T, K> convertor) {
return convertor.apply(getOne(queryWrapper, true));
}
/**
* 查询列表
*
* @param kClass vo类型
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
default <K> List<K> listVo(Wrapper<T> queryWrapper, Class<K> kClass) {
List<T> list = getBaseMapper().selectList(queryWrapper);
if (list == null) {
return null;
}
return list.stream()
.map(any -> BeanUtils.toBean(any, kClass))
.collect(Collectors.toList());
}
/**
* 查询列表
*
* @param convertor 转换函数
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
default <K> List<K> listVo(Wrapper<T> queryWrapper, Function<Collection<T>, List<K>> convertor) {
List<T> list = getBaseMapper().selectList(queryWrapper);
if (list == null) {
return null;
}
return convertor.apply(list);
}
/**
* 查询所有
*
* @param kClass vo类型
* @see Wrappers#emptyWrapper()
*/
default <K> List<K> listVo(Class<K> kClass) {
return listVo(Wrappers.emptyWrapper(), kClass);
}
/**
* 查询所有
*
* @param convertor 转换函数
* @see Wrappers#emptyWrapper()
*/
default <K> List<K> listVo(Function<Collection<T>, List<K>> convertor) {
return listVo(Wrappers.emptyWrapper(), convertor);
}
/**
* 无条件翻页查询(通过原生Page) 转换为VO
*
* @param page 翻页对象
*/
default <K> Page<K> pageVo(Page<T> page, Class<K> kClass) {
return pageVo(page, Wrappers.emptyWrapper(), kClass);
}
/**
* 无条件翻页查询(通过原生Page) 转换为VO
*
* @param page 翻页对象
* @param convertor 转换函数
*/
default <K> Page<K> pageVo(Page<T> page, Function<Collection<T>, List<K>> convertor) {
return pageVo(page, Wrappers.emptyWrapper(), convertor);
}
/**
* 翻页查询(通过原生Page) 转换为VO
*
* @param page 翻页对象
* @param queryWrapper 实体对象封装操作类
* @param convertor 转换函数
*/
default <K> Page<K> pageVo(Page<T> page, Wrapper<T> queryWrapper,
Function<Collection<T>, List<K>> convertor) {
Page<T> result = getBaseMapper().selectPage(page, queryWrapper);
Page<K> kResult = (Page<K>) result;
kResult.setRecords(convertor.apply(result.getRecords()));
return kResult;
}
/**
* 翻页查询(通过原生Page) 转换为VO
*
* @param page 翻页对象
* @param queryWrapper 实体对象封装操作类
*/
default <K> Page<K> pageVo(Page<T> page, Wrapper<T> queryWrapper, Class<K> kClass) {
Page<T> result = getBaseMapper().selectPage(page, queryWrapper);
return coverPage(result, kClass);
}
default Page<T> pageVo(PageQuery query) {
return pageVo(query, Wrappers.emptyWrapper());
}
default Page<T> pageVo(PageQuery query, Wrapper<T> queryWrapper) {
return getBaseMapper().selectPage(convertPage(query), queryWrapper);
}
/**
* @param query
* @param kClass
* @Description: 无条件翻页查询(通过PageQuery转换) 转换为VO
* @return: com.baomidou.mybatisplus.extension.plugins.pagination.Page<K>
* @date: 2021/08/08
*/
default <K> Page<K> pageVo(PageQuery query, Class<K> kClass) {
return pageVo(convertPage(query), kClass);
}
/**
* @param query
* @param convertor
* @Description: 无条件翻页查询(通过PageQuery转换) 转换为VO
* @return: com.baomidou.mybatisplus.extension.plugins.pagination.Page<K>
* @date: 2021/08/08
*/
default <K> Page<K> pageVo(PageQuery query, Function<Collection<T>, List<K>> convertor) {
return pageVo(convertPage(query), convertor);
}
/**
* @param query
* @param queryWrapper
* @param convertor
* @Description: 翻页查询(通过PageQuery转换) 转换为VO
* @return: com.baomidou.mybatisplus.extension.plugins.pagination.Page<K>
* @date: 2021/08/08
*/
default <K> Page<K> pageVo(PageQuery query, Wrapper<T> queryWrapper,
Function<Collection<T>, List<K>> convertor) {
return pageVo(convertPage(query), queryWrapper, convertor);
}
/**
* 翻页查询(通过PageQuery转换) 转换为VO
*
* @param query 翻页对象
* @param queryWrapper 实体对象封装操作类
*/
default <K> Page<K> pageVo(PageQuery query, Wrapper<T> queryWrapper, Class<K> kClass) {
return pageVo(convertPage(query), queryWrapper, kClass);
}
/**
* @param page
* @Description: 查询PagerVo分页对象(通过原生Page)
* @return: com.lly.datacover.page.Pager<T>
* @date: 2021/08/08
*/
default Pager<T> pager(Page<T> page) {
return coverPager(page(page));
}
/**
* @param page
* @Description: 查询PagerVo分页对象(通过原生Page)
* @return: com.lly.datacover.page.Pager<T>
* @date: 2021/08/08
*/
default Pager<T> pager(Page<T> page, Wrapper<T> queryWrapper) {
return coverPager(page(page, queryWrapper));
}
/**
* @param page
* @param convertor
* @Description: 查询PagerVo分页对象(通过原生Page) 转换为VO
* @return: com.lly.web.model.dto.Pager<T>
* @date: 2021/07/30
*/
default <K> Pager<K> pagerVo(Page<T> page, Function<Collection<T>, List<K>> convertor) {
return coverPager(pageVo(page, convertor));
}
/**
* @param page
* @param queryWrapper
* @param convertor
* @Description: 查询PagerVo分页对象(通过原生Page) 转换为VO
* @return: com.lly.web.model.dto.Pager<T>
* @date: 2021/07/30
*/
default <K> Pager<K> pagerVo(Page<T> page, Wrapper<T> queryWrapper,
Function<Collection<T>, List<K>> convertor) {
return coverPager(pageVo(page, queryWrapper, convertor));
}
/**
* @param page
* @param kClass
* @Description: 查询PagerVo分页对象(通过原生Page) 转换为VO
* @return: com.lly.web.model.dto.Pager<T>
* @date: 2021/07/30
*/
default <K> Pager<K> pagerVo(Page<T> page, Class<K> kClass) {
return coverPager(pageVo(page, kClass));
}
/**
* @param page
* @param queryWrapper
* @param kClass
* @Description: 查询PagerVo分页对象(通过原生Page) 转换为VO
* @return: com.lly.web.model.dto.Pager<T>
* @date: 2021/07/30
*/
default <K> Pager<K> pagerVo(Page<T> page, Wrapper<T> queryWrapper, Class<K> kClass) {
return coverPager(pageVo(page,queryWrapper,kClass));
}
/**
* @param query
* @Description: 查询PagerVo分页对象(通过PageQuery转换)
* @return: com.lly.datacover.page.Pager<T>
* @date: 2021/08/08
*/
default Pager<T> pager(PageQuery query) {
return coverPager(pageVo(query));
}
/**
* @param query
* @param queryWrapper
* @Description: 查询PagerVo分页对象(通过PageQuery转换)
* @return: com.lly.datacover.page.Pager<T>
* @date: 2021/08/08
*/
default Pager<T> pager(PageQuery query, Wrapper<T> queryWrapper) {
return coverPager(pageVo(query,queryWrapper));
}
/**
* @param query
* @param convertor
* @Description: 查询PagerVo分页对象(通过PageQuery转换) 转换为VO
* @return: com.lly.datacover.page.Pager<T>
* @date: 2021/08/08
*/
default <K>Pager<K> pagerVo(PageQuery query, Function<Collection<T>, List<K>> convertor) {
return coverPager(pageVo(query, convertor));
}
/**
* @param query
* @param queryWrapper
* @param convertor
* @Description: 查询PagerVo分页对象(通过PageQuery转换) 转换为VO
* @return: com.lly.datacover.page.Pager<T>
* @date: 2021/08/08
*/
default <K>Pager<K> pagerVo(PageQuery query, Wrapper<T> queryWrapper,
Function<Collection<T>, List<K>> convertor) {
return coverPager(pageVo(query,queryWrapper,convertor));
}
/**
* @param query
* @param kClass
* @Description: 查询PagerVo分页对象(通过PageQuery转换) 转换为VO
* @return: com.lly.datacover.page.Pager<T>
* @date: 2021/08/08
*/
default <K>Pager<K> pagerVo(PageQuery query, Class<K> kClass) {
return coverPager(pageVo(query,kClass));
}
/**
* @param query
* @param queryWrapper
* @param kClass
* @Description: 查询PagerVo分页对象(通过PageQuery转换) 转换为VO
* @return: com.lly.datacover.page.Pager<T>
* @date: 2021/08/08
*/
default <K> Pager<K> pagerVo(PageQuery query, Wrapper<T> queryWrapper, Class<K> kClass) {
return coverPager(pageVo(query,queryWrapper,kClass));
}
@Override
default boolean saveBatch(Collection<T> entityList) {
return saveBatch(entityList, DEFAULT_BATCH_SIZE);
}
@Override
default boolean saveOrUpdateBatch(Collection<T> entityList) {
return saveOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE);
}
@Override
default boolean updateBatchById(Collection<T> entityList) {
return updateBatchById(entityList, DEFAULT_BATCH_SIZE);
}
/**
* @Description: 将page<K> 转换为pager<K> K为泛型
* @param page
* @return: com.lly.datacover.page.Pager<K>
* @date: 2021/08/09
*/
default <K> Pager<K> coverPager(Page<K> page) {
Pager<K> pager = new Pager<>();
pager.setPagenum((int) page.getCurrent());
pager.setPagesize((int) page.getSize());
pager.setData(page.getRecords());
return pager;
}
/**
* @Description: 将page<po>转换为pager<vo>
* @param page
* @param kClass
* @return: com.lly.datacover.page.Pager<K>
* @date: 2021/08/09
*/
default <K> Pager<K> coverPager(Page<T> page, Class<K> kClass) {
Pager<K> pager = new Pager<>();
pager.setPagenum((int) page.getCurrent());
pager.setPagesize((int) page.getSize());
List<K> volist = page.getRecords().stream()
.map(any -> BeanUtils.toBean(any, kClass))
.collect(Collectors.toList());
pager.setData(volist);
return pager;
}
/**
* @param query
* @Description: 将pageQuery对象转换为page对象
* @return: com.baomidou.mybatisplus.extension.plugins.pagination.Page<T>
* @date: 2021/08/08
*/
default Page<T> convertPage(PageQuery query) {
Page<T> page = null;
if (null != query){
page = new Page<>(query.getPagenum(),query.getPagenum());
Order[] orders = query.getOrders();
if (orders.length > 0){
List<OrderItem> collect = Arrays.stream(orders).map(order ->
order.getOrdertype().equals(Order.Type.ASC) ?
OrderItem.asc(order.getOrderby()) : OrderItem.desc(order.getOrderby()))
.collect(Collectors.toList());
page.setOrders(collect);
}
}
return page;
}
/**
* @Description: 将page中的实体转换为Vo
* @param page
* @param kClass
* @return: com.baomidou.mybatisplus.extension.plugins.pagination.Page<K>
* @date: 2021/08/09
*/
default <K> Page<K> coverPage(Page<T> page, Class<K> kClass) {
List<K> volist = page.getRecords().stream()
.map(any -> BeanUtils.toBean(any, kClass))
.collect(Collectors.toList());
Page<K> Page = (Page<K>) page;
Page.setRecords(volist);
return Page;
}
}BaseService
package com.lly.db.mybatisplus.core.service;
import com.baomidou.mybatisplus.core.toolkit.ClassUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lly.db.mybatisplus.core.mapper.IBaseDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ResolvableType;
import java.util.Collection;
/**
* @Classname BaseService
* @Description service基类
* @Date 2021/07/18 15:27
* @Created by wxp
*/
public class BaseService<M extends IBaseDao<T>, T> extends ServiceImpl<M, T> implements IService<T> {
@Autowired
protected M baseMapper;
@Override
public M getBaseMapper() {
return baseMapper;
}
protected Class<T> entityClass = currentModelClass();
@Override
public Class<T> getEntityClass() {
return entityClass;
}
protected Class<T> mapperClass = currentMapperClass();
@Override
protected Class<T> currentMapperClass() {
return (Class<T>) this.getResolvableType().as(BaseService.class).getGeneric(0).getType();
}
@Override
protected Class<T> currentModelClass() {
return (Class<T>) this.getResolvableType().as(BaseService.class).getGeneric(1).getType();
}
@Override
protected ResolvableType getResolvableType() {
return ResolvableType.forClass(ClassUtils.getUserClass(getClass()));
}
/**
* 单条执行性能差
* <p>
* {@link #saveAll(Collection)}
*/
@Deprecated
@Override
public boolean saveBatch(Collection<T> entityList, int batchSize) {
return super.saveBatch(entityList, batchSize);
}
@Override
public boolean saveOrUpdate(T entity) {
return super.saveOrUpdate(entity);
}
/**
* 单条执行性能差
* <p>
* {@link #saveAll(Collection)}
*/
@Deprecated
@Override
public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
return super.saveOrUpdateBatch(entityList, batchSize);
}
@Override
public boolean updateBatchById(Collection<T> entityList, int batchSize) {
return super.updateBatchById(entityList, batchSize);
}
@Override
public boolean saveAll(Collection<T> entityList) {
return baseMapper.insertAll(entityList) == entityList.size();
}
}2.3 多数据源
依赖
compile 'com.baomidou:dynamic-datasource-spring-boot-starter:3.4.0'配置
spring:
datasource:
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
dynamic:
#设置默认的数据源或者数据源组,默认值即为 master
primary: master
datasource:
# 主库数据源
master:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/ruoyi_vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
username: root
password: 1234
# 从库数据源
slave:
driverClassName: com.mysql.cj.jdbc.Driver
url:
username:
password:2.4 插件自动生成代码
依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>implementation 'org.freemarker:freemarker:2.3.28'工具类
package com.note.technology.mybatisplus.antogenerator;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class CodeGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotEmpty(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("作者");
gc.setOpen(false);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://loclhost:3306/demo?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(scanner("模块名"));
pc.setParent("com.demo");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
List<FileOutConfig> focList = new ArrayList<>();
focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输入文件名称
return projectPath + "/src/main/resources/mapper/" +pc.getModuleName()+ "/" + tableInfo.getEntityName() + "Mapper" +StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
mpg.setTemplate(new TemplateConfig().setXml(null));
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// strategy.setSuperEntityClass("继承父类");
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
//strategy.setSuperControllerClass("com.baomidou.ant.common.BaseController");
strategy.setInclude(scanner("表名"));
strategy.setSuperEntityColumns("id");
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}2.5 与spring整合
依赖
<!-- mybatis-plus插件依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.1.1</version>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.11</version>
</dependency>
<!--简化bean代码的工具包-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>1.18.4</version>
</dependency>配置
# jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mp?
useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL
=false
jdbc.username=root
jdbc.password=rootspring配置
<context:property-placeholder location="classpath:*.properties"/>
<!-- 定义数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="maxActive" value="10"/>
<property name="minIdle" value="5"/>
</bean>
<!--这里使用MP提供的sqlSessionFactory,完成了Spring与MP的整合-->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--扫描mapper接口,使用的依然是Mybatis原生的扫描器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.itcast.mp.simple.mapper"/>
</bean>
</beans>三、基础知识
3.1 通用Mapper
mybatis-plus提供了一个基础的mapper类com.baomidou.mybatisplus.core.mapper.BaseMappermapper接口继承该接口即可获取基础的crud的操作方法。
基础方法有:增删改查(通过id、wrapper对象和map数据)、查询map、boject、分页Map数据、查询数量、查询单条数据、批量处理等方法。
3.2 常用注解
@TableField
在MP中通过@TableField注解可以指定字段的一些属性,常常解决的问题有2个:
1、对象中的属性名和字段名不一致的问题(非驼峰)
2、对象中的属性字段在表中不存在的问题
@TableName
指定数据库表名
3.3 id生成策略
package com.baomidou.mybatisplus.annotation;
import lombok.Getter;
/**
* 生成ID类型枚举类
*
* @author hubin
* @since 2015-11-10
*/
@Getter
public enum IdType {
/**
* 数据库ID自增
*/
AUTO(0),
/**
* 该类型为未设置主键类型
*/
NONE(1),
/**
* 用户输入ID
* <p>该类型可以通过自己注册自动填充插件进行填充</p>
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 全局唯一ID (idWorker)
*/
ID_WORKER(3),
/**
* 全局唯一ID (UUID)
*/
UUID(4),
/**
* 字符串全局唯一ID (idWorker 的字符串表示)
*/
ID_WORKER_STR(5);
private final int key;
IdType(int key) {
this.key = key;
}
}使用方式,在model代表的表的主键字段上添加注解@TableId(type = IdType.AUTO) //指定id类型为自增长
3.4 条件查询
分页查询
分页查询需在配置类上添加对应的分页插件
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("cn.itcast.mp.mapper") //设置mapper接口的扫描包
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}查询对象
在MP中,Wrapper接口存在多种实现,其中AbstractWrapper和AbstractChainWrapper是重点实现。官网文档地址:https://mybatis.plus/guide/wrapper.html
主要方法
java// 方法可通过wrapper进行链式调用 // allEq // 全部eq(或个别isNull) // params : key 为数据库字段名, value 为字段值 // null2IsNull : 为 true 则在 map 的 value 为null 时调用 isNull 方法,为 false 时则忽略 value 为 null 的 allEq(Map<R, V> params, boolean null2IsNull); // filter : 过滤函数,是否允许字段传入比对条件中 allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull); // eq 等于 = // ne 不等于 <> // gt 大于 > // ge 大于等于 >= // lt 小于 < // le 小于等于 <= // between BETWEEN 值1 AND 值2 // notBetween NOT BETWEEN 值1 AND 值2 // in 字段 IN (value.get(0), value.get(1), ...) // like LIKE '%值%' notLike likeLeft '%值' likeRight '值%' // orderBy 默认asc 升序 orderByAsc orderByDesc (降序) // or 拼接 OR 链接判断的eq、ne等逻辑判断 // and AND 嵌套 // select 在MP查询中,默认查询所有的字段,如果有需要也可以通过select方法进行指定字段。查询对象
javaQueryWrapper wrapper = new QueryWrapper();
3.5 ActiveRecord
ActiveRecord(简称AR)一直广受动态语言( PHP 、 Ruby 等)的喜爱,而 Java 作为准静态语言,对于 ActiveRecord 往往只能感叹其优雅
什么是ActiveRecord?
ActiveRecord也属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记 录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而 且简洁易懂。 ActiveRecord的主要思想是: 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段 在类中都有相应的Field; ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;; ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑;
实体类继承Model
实体类继承
com.baomidou.mybatisplus.extension.activerecord.Model;javaimport com.baomidou.mybatisplus.extension.activerecord.Model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class User extends Model<User> { private Long id; private String name; private Integer age; }增删改查
java// 通过id查询 User user = new User(); user.setId(2L); User user2 = user.selectById(); // 新增 User user = new User(); user.setName("刘备"); user.setAge(30); boolean insert = user.insert(); // 更新 User user = new User(); user.setId(8L); user.setAge(35); boolean update = user.updateById(); // 删除 User user = new User(); user.setId(7L); boolean delete = user.deleteById(); // 条件查询 User user = new User(); QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper.le("age","20"); List<User> users = user.selectList(userQueryWrapper);
3.6 Oracle主键
oracle不能使用主键自增长,需要使用Sequence 序列生成id值。
oracle驱动包
xml<dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc8</artifactId> <version>12.1.0.1</version> </dependency>配置
properties#数据库连接配置 spring.datasource.driver-class-name=oracle.jdbc.OracleDriver spring.datasource.url=jdbc:oracle:thin:@192.168.31.81:1521:xe spring.datasource.username=system spring.datasource.password=oracle #id生成策略 mybatis-plus.global-config.db-config.id-type=input配置序列
java/** * 序列生成器 */ @Bean public OracleKeyGenerator oracleKeyGenerator(){ return new OracleKeyGenerator(); }在实体类中指定序列的名称
java@KeySequence(value = "SEQ_USER", clazz = Long.class) public class User{ ...... }注意,新增数据的主键值会填到实体类的对象中。
3.7 逻辑删除
在需要逻辑删除的表中添加逻辑删除字段,并在逻辑删除对应的字段上添加@TableLogic注解
并添加配置
# 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-delete-value=1
# 逻辑未删除值(默认为 0)
mybatis-plus.global-config.db-config.logic-not-delete-value=03.8 枚举属性
mybatisplus可将实体类中的枚举类型获取到它的值拼接到sql的值中。增删改查均有效。
需添加配置
# 枚举包扫描
mybatis-plus.type-enums-package=cn.itcast.mp.enums四、拓展
4.1 配置
配置文件
MyBatis 配置文件位置,如果您有单独的 MyBatis 配置,请将其路径配置到 configLocation 中。 MyBatis Configuration 的具体内容请参考MyBatis 官方文档
# springboot
mybatis-plus.config-location = classpath:mybatis-config.xml<!--springMVC-->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>mapperLocations
如果Mapper中存在自定义方法,需要告知mapperxml的位置
# springboot
mybatis-plus.mapper-locations = classpath*:mybatis/*.xml<!-- springmvc -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="mapperLocations" value="classpath*:mybatis/*.xml"/>
</bean>typeAliasesPackage
MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在 Mapper 对应的 XML 文件中可以直接使 用类名,而不用使用全限定的类名(即 XML 中调用的时候不用包含包名)。
# springboot
mybatis-plus.type-aliases-package = cn.itcast.mp.pojo<!--springmvc-->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="typeAliasesPackage" value="com.baomidou.mybatisplus.samples.quickstart.entity"/>
</bean>mapUnderscoreToCamelCase
是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属 性名 aColumn(驼峰命名) 的类似映射。
注意:
此属性在 MyBatis 中原默认值为 false,在 MyBatis-Plus 中,此属性也将用于生成最终的 SQL 的 select body
如果您的数据库命名符合规则无需使用 @TableField 注解指定数据库字段名
# springboot
#关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在
mybatis-plus.configuration.map-underscore-to-camel-case=falsecacheEnabled
全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true。
mybatis-plus.configuration.cache-enabled=falseidType
全局默认主键类型,设置后,即可省略实体对象中的@TableId(type = IdType.AUTO)配置。
# springboot
mybatis-plus.global-config.db-config.id-type=auto<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="globalConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
<property name="idType" value="AUTO"/>
</bean>
</property>
</bean>
</property>
</bean>tablePrefix
表名前缀,全局配置后可省略@TableName()配置。
# springboot
mybatis-plus.global-config.db-config.table-prefix=tb_<!--springmvc-->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="globalConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
<property name="idType" value="AUTO"/>
<property name="tablePrefix" value="tb_"/>
</bean>
</property>
</bean>
</property>
</bean>log-impl
查阅执行期SQL语句
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl4.2 插件
mybatis允许在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法 调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
可以拦截Executor接口的部分方法,比如update,query,commit,rollback等方法,还有其他接口的 一些方法等。 总体概括为:
- 拦截执行器的方法
- 拦截参数的处理
- 拦截结果集的处理
- 拦截Sql语法构建的处理
拦截器定义示例
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import java.util.Properties;
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class MyInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//拦截方法,具体业务逻辑编写的位置
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
//创建target对象的代理对象,目的是将当前拦截器加入到该对象中
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
//属性设置
}
}// 注入到spring容器
/**
* 自定义拦截器
*/
@Bean
public MyInterceptor myInterceptor(){
return new MyInterceptor();
}<!--配置方式-->
<configuration>
<plugins>
<plugin interceptor="cn.itcast.mp.plugins.MyInterceptor"></plugin>
</plugins>
</configuration>执行分析插件
在MP中提供了对SQL执行的分析的插件,可用作阻断全表更新、删除的操作
@Bean
public SqlExplainInterceptor sqlExplainInterceptor(){
SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
List<ISqlParser> sqlParserList = new ArrayList<>();
// 攻击 SQL 阻断解析器、加入解析链
sqlParserList.add(new BlockAttackSqlParser());
sqlExplainInterceptor.setSqlParserList(sqlParserList);
return sqlExplainInterceptor;
}性能分析插件
性能分析拦截器,用于输出每条 SQL 语句及其执行时间,可以设置最大执行时间,超过时间会抛出异常。不建议生产使用
<configuration>
<plugins>
<!-- SQL 执行性能分析,开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长 -->
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
<property name="maxTime" value="100" />
<!--SQL是否格式化 默认false-->
<property name="format" value="true" />
</plugin>
</plugins>
</configuration>乐观锁插件
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion 如果version不对,就更新失败
<!--spring-->
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>// springboot
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}使用方式
// 1. 在表中添加version字段
// 2. 在实体类对象添加version对应的字段和标注@Version注解支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
整数类型下 newVersion = oldVersion + 1 newVersion 会回写到 entity 中
仅支持 updateById(id) 与 update(entity, wrapper) 方法
在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
4.3 sql注入器(实现拓展BaseMapper方法)
编写BaseMapper类
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
public interface MyBaseMapper<T> extends BaseMapper<T> {
List<T> findAll();
}拓展后,其他mapper需要继承该mapper
编写sqlInjector
直接继承AbstractSqlInjector的话,原有的BaseMapper中的方法将失效,所以我们选择继承DefaultSqlInjector 进行扩展。
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import java.util.List;
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList() {
List<AbstractMethod> methodList = super.getMethodList();
methodList.add(new FindAll());
// 再扩充自定义的方法
list.add(new FindAll());
return methodList;
}
}编写findall逻辑
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
public class FindAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sqlMethod = "findAll";
String sql = "select * from " + tableInfo.getTableName();
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return this.addSelectMappedStatement(mapperClass, sqlMethod, sqlSource, modelClass, tableInfo);
}
}将sql注入器注入spring容器
/**
* 自定义SQL注入器
*/
@Bean
public MySqlInjector mySqlInjector(){
return new MySqlInjector();
}4.4 自动填充功能(插入或更新时自动填充某些字段数据)
添加@TableField注解
//插入数据时进行填充 DEFAULT 默认不处理 INSERT 插入时填充 UPDATE 更新时填充 INSERT_UPDATE 插入和更新时填充字段
@TableField(fill = FieldFill.INSERT)
private String password;编写MetaObjectHandler
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
Object password = getFieldValByName("password", metaObject);
if(null == password){
//字段为空,可以进行填充
setFieldValByName("password", "123456", metaObject);
}
}
@Override
public void updateFill(MetaObject metaObject) {
}
}